package com.tonyodev.fetchapp.fetch2fileserver;

import ohos.aafwk.ability.delegation.AbilityDelegatorRegistry;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.RawFileEntry;
import ohos.global.resource.ResourceManager;

import com.tonyodev.fetch2core.FetchCoreUtils;
import com.tonyodev.fetch2core.FileResource;
import com.tonyodev.fetch2core.MutableExtras;
import com.tonyodev.fetch2core.server.FileRequest;
import com.tonyodev.fetch2fileserver.AbstractFetchFileServerDelegate;
import com.tonyodev.fetch2fileserver.FetchFileServer;
import com.tonyodev.fetch2fileserver.FetchFileServerAuthenticator;
import com.tonyodev.fetch2fileserver.FetchFileServerDelegate;
import com.tonyodev.fetch2fileserver.FetchFileServerImpl;
import com.tonyodev.fetch2fileserver.FetchFileServerLogger;
import com.tonyodev.fetch2fileserver.FetchTransferListener;
import com.tonyodev.fetch2fileserver.FileResolver;
import com.tonyodev.fetch2fileserver.database.FetchFileResourceInfoDatabase;
import com.tonyodev.fetch2fileserver.database.FileResourceExtensionsKt;
import com.tonyodev.fetchapp.Data;
import com.tonyodev.fetchapp.Utils;

import org.junit.After;
import org.junit.Before;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;

import junit.framework.TestCase;

import static com.tonyodev.fetch2core.FetchCoreDefaults.DEFAULT_PERSISTENT_TIME_OUT_IN_MILLISECONDS;
import static com.tonyodev.fetch2core.FetchCoreDefaults.DEFAULT_PROGRESS_REPORTING_INTERVAL_IN_MILLISECONDS;
import static com.tonyodev.fetchapp.ability.FileServerAbility.CONTENT_PATH;

public class FetchFileServerImplTest extends TestCase {
    private Context appContext;
    private FetchFileServer fetchFileServer;
    private FetchFileResourceInfoDatabase fetchFileResourceInfoDatabase;
    private EventHandler ioHandler;
    private Object lock = new Object();
    private boolean isTerminated = false;

    @Before
    public void setUp() throws Exception {
        appContext = AbilityDelegatorRegistry.getAbilityDelegator().getAppContext();
        ioHandler = new EventHandler(EventRunner.create());
        final ServerSocket serverSocket = new ServerSocket(0);
        final boolean clearDatabaseOnShutdown = false;
        final FetchFileServerLogger logger = new FetchFileServerLogger();
        final FetchFileServerAuthenticator fileServerAuthenticator = new FetchFileServerAuthenticator() {
            @Override
            public boolean accept(String sessionId, String authorization, FileRequest fileRequest) {
                return false;
            }
        };
        final FetchFileServerDelegate fileServerDelegate = new AbstractFetchFileServerDelegate();
        final FetchTransferListener transferListener = new FetchTransferListener() {
            @Override
            public void onStarted(String sessionId, FileRequest fileRequest, FileResource fileResource) {

            }

            @Override
            public void onProgress(String sessionId, FileRequest fileRequest, FileResource fileResource, int progress) {

            }

            @Override
            public void onComplete(String sessionId, FileRequest fileRequest, FileResource fileResource) {

            }

            @Override
            public void onError(String sessionId, FileRequest fileRequest, FileResource fileResource, Throwable throwable) {

            }
        };
        final String fileResourceDatabaseName = "LibFetchFileServerDatabaseLib.db";
        final long progressReportingInMillis = DEFAULT_PROGRESS_REPORTING_INTERVAL_IN_MILLISECONDS;
        final long persistentConnectionTimeout = DEFAULT_PERSISTENT_TIME_OUT_IN_MILLISECONDS;
        final FileResolver fileResolver = new FileResolver(appContext.getApplicationContext());

        fetchFileServer = new FetchFileServerImpl(appContext, serverSocket, clearDatabaseOnShutdown,
            logger, fileResourceDatabaseName, fileServerAuthenticator,
            fileServerDelegate, transferListener, progressReportingInMillis,
            persistentConnectionTimeout, fileResolver);
        fetchFileResourceInfoDatabase = new FetchFileResourceInfoDatabase(appContext, fileResourceDatabaseName);
    }

    @After
    public void tearDown() throws Exception {
    }

    public void testStart() {
        try {
             fetchFileServer.start();
            assertTrue(true);
        } catch (Exception e) {
            assertTrue(false);;
        }

    }

    public void testAddFileResource() {
        synchronized (lock) {
            try {
                throwIfTerminated();
                ioHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        long readBytes = 0;
                        final String testFilePath = Data.getSaveDir(appContext) + "/source/" + CONTENT_PATH;
                        final File file = Utils.createFile(testFilePath);
                        final ResourceManager resourceManager = appContext.getResourceManager();
                        final RawFileEntry rawFileEntry = resourceManager.getRawFileEntry("resources/rawfile/test_file.db");
                        try {
                            InputStream inputStream = new BufferedInputStream(rawFileEntry.openRawFile());
                            OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
                            byte[] buffer = new byte[1024];
                            int read;
                            while (((read = inputStream.read(buffer, 0, 1024)) != -1)) {
                                readBytes += read;
                                outputStream.write(buffer, 0, read);
                            }
                            inputStream.close();
                            outputStream.flush();
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        final FileResource fileResource = new FileResource();
                        try {
                            fileResource.setFile(file.getCanonicalPath());
                            fileResource.setName(file.getName());
                            fileResource.setId(file.getCanonicalPath().hashCode());
                            fileResource.setLength(readBytes);
                            final String fileMd5 = FetchCoreUtils.getFileMd5String(file.getCanonicalPath());
                            if (fileMd5 != null) {
                                fileResource.setMd5(fileMd5);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        final MutableExtras extras = new MutableExtras();
                        extras.putLong("contentLength", readBytes);
                        extras.putString("contentName", file.getName());
                        extras.putString("contentSampleKey", "contentSampleText");
                        fileResource.setExtras(extras.toExtras());
                        throwIfAddingReservedCatalogInfo(fileResource);
                        if (fileResource.getMd5().isEmpty()) {
                            fileResource.setMd5(getMd5CheckSumForFileResource(fileResource));
                        }
                        fetchFileResourceInfoDatabase.insert(FileResourceExtensionsKt.toFileResourceInfo(fileResource));
                        assertNotNull(fetchFileResourceInfoDatabase.get());
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private void throwIfTerminated() throws Exception {
        if (isTerminated) {
            throw new Exception("FetchFileServer was already Shutdown. It cannot be restarted. Get a new Instance.");
        }
    }

    private void throwIfAddingReservedCatalogInfo(FileResource fileResource) {
        if (fileResource.getId() == FileRequest.CATALOG_ID
            || fileResource.getName() == FileRequest.CATALOG_NAME
            || fileResource.getFile() == FileRequest.CATALOG_FILE) {
            throw new IllegalArgumentException("File Resources 'id' cannot be: ${FileRequest.CATALOG_ID} " +
                "and 'name' cannot be: Catalog.json and " +
                "'file' cannot be: /Catalog.json");
        }
    }

    private String getMd5CheckSumForFileResource(FileResource fileResource) {
        return FetchCoreUtils.getFileMd5String(fileResource.getFile()) != null ? FetchCoreUtils.getFileMd5String(fileResource.getFile()) : "";
    }
}